Solver Errors in CVXPY

David Stonestrom 6/4/2014


Writing a tutorial that demonstrates dealing with solver errors is tricky because the people who write/maintain the solvers are constantly updating the solvers to deal with known issues. There is a solid chance that by the time you are reading this the code below will no longer generate errors. However, the notes which accompany the code are still the correct procedure for handling solver errors. As of the time of this writing, the exact problem below resulted in three different outcomes depending on which solver was used. The problem is:

$$ \textbf{maximize } f\left(x\right) = p^{T}\sqrt{x}$$$$\text{subject to } Ax \preceq b$$


where $A$ is one row and has one negative entry and $p \succeq 0$. The negative entry in $A$ makes the problem unbounded, so the correct solution is:

objective value: inf

status: unbounded


In [1]:
import cvxpy as cvx
import numpy

n = 6;  
m = 1; 

# generate some constraints
numpy.random.seed(0) # for repeatibility
A = numpy.random.randn(m,n)
b = 10 * numpy.random.random([m,1])

p = cvx.Parameter(1,n,nonneg=True)
p.value = numpy.random.random([1,n]) # returnes values in the interval [0.0, 1.0)

x = cvx.Variable(n,1) # (n,1) will give a colum vector

objective = cvx.Maximize(p * cvx.sqrt(x))
constraint = [A*x <= b]
problem = cvx.Problem(objective, constraint)

print "A: ", A
print "b: ", b
print "p: ", p.value


A:  [[ 1.76405235  0.40015721  0.97873798  2.2408932   1.86755799 -0.97727788]]
b:  [[ 4.37587211]]
p:  [[ 0.891773    0.96366276  0.38344152  0.79172504  0.52889492  0.56804456]]
/Users/stevend2/anaconda/envs/cvxpy/lib/python2.7/site-packages/ecos-1.0.4-py2.7-macosx-10.5-x86_64.egg/_ecos.py:3: UserWarning: Module cvxpy was already imported from /Users/stevend2/anaconda/envs/cvxpy/lib/python2.7/site-packages/cvxpy-0.2.0-py2.7.egg/cvxpy/__init__.pyc, but /Users/stevend2/PythonProjects/cvxpy/examples/notebooks is being added to sys.path

ECOS


In [2]:
try:
    print problem.solve(solver = cvx.ECOS) # the default
    print problem.status
except Exception as e:
    print e


None
solver_error

This is the classic solver error outcome. The objective is 'None' and the status is 'solver_error.' Note that it did not raise an exception.

When this happens, the next step is to just try the other available solvers to see what they do.

CVXOPT


In [3]:
try:
    print problem.solve(solver=cvx.CVXOPT)
    print problem.status
except Exception as e:
    print e


math domain error

Using CVXOPT throws an exception: 'math domain error.' This will probably be rolled into the solver_error case above at some point.

SCS is the only solver left to try.

SCS


In [4]:
try:
    print problem.solve(solver=cvx.SCS)
    print problem.status
except Exception as e:
    print e


2082.01956591
optimal

Here the numerical imprecision of SCS lets it find an optimal value. Note the printed objective value: 2082. Compare it to the numpy computed objective for the optimal value of x below:


In [5]:
print "numpy calculated objective value:", p.value.dot(numpy.sqrt(x.value))


numpy calculated objective value: [[ 2322.27407977]]

The two values differ by over 10%. Next is a demonstration of a better feasible point.


In [6]:
print "SCS objective value: ", p.value.dot(numpy.sqrt(x.value))
print "SCS constraint violation: ", A.dot(x.value) - b

x_better = x.value + [[0],[0],[9900000],[0],[0],[10000000]]
print "\nHigher objective and lower constraint violation:"
print "objective: ", p.value.dot(numpy.sqrt(x_better))
print "constraint violation: ", A.dot(x_better) - b


SCS objective value:  [[ 2322.27407977]]
SCS constraint violation:  [[ 0.00387292]]

Higher objective and lower constraint violation:
objective:  [[ 4672.30778292]]
constraint violation:  [[-83272.75224437]]

Another problem

Compare the above results to the following unbounded problem which ECOS solves corectly.

$$ \textbf{maximize } f\left(x\right) = \sum_{i=1}^{6}\sqrt{x_i}$$

In [7]:
x = cvx.Variable(6)
problem = cvx.Problem(cvx.Maximize(cvx.sum(cvx.sqrt(x))))

try:
    print "ECOS:\n", problem.solve(solver=cvx.ECOS)
    print problem.status
    print "\nSCS:\n", problem.solve(solver=cvx.SCS)
    print problem.status
    print "\nCVXOPT:\n", problem.solve(solver=cvx.CVXOPT)
    print problem.status
    
except Exception as e:
    print "Error Error Error"
    print e


ECOS:
inf
unbounded

SCS:
3257.630475
optimal

CVXOPT:
Error Error Error
math domain error

The takeaway from all of this is to try all the solvers and make sure that you believe the answer they give. One of the important pieces of this is manually testing the constraint violations and objective value. Also consider what the value of the objective and variables are saying about the origional problem.

Finally, please keep in mind that this is an open source project and is provided "as is" with no guarintee of correctness.